package com.easyaop.impl;

import com.easyaop.api.Context;
import com.easyaop.api.EasyAopTemple;
import com.easyaop.api.advisor.Advisor;
import com.easyaop.api.advisor.AroundAdvisor;
import com.easyaop.api.advisor.AspectjExpressionPointcut;
import com.easyaop.api.advisor.EventAdvisor;
import com.easyaop.api.around.Around;
import com.easyaop.api.event.listener.MethodListener;
import com.easyaop.api.pointcut.MethodPointcut;
import com.easyaop.api.pointcut.Pointcut;
import com.easyaop.core.event.Event;
import com.easyaop.core.event.EventSource;
import com.easyaop.core.event.PointcutEvent;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

public class EasyAopTempleImpl implements EasyAopTemple {
    @Override
    public void add(String expression, Around around) {
        doAdd(AspectjExpressionPointcut.of(expression), around);
    }

    @Override
    public void add(Method method, Around around) {
        doAdd(MethodPointcut.of(method), around);
    }

    @Override
    public void add(Pointcut pointcut, Around around) {
        doAdd(pointcut, around);
    }

    @Override
    public boolean remove(String expression, Class<? extends Around>... aroundTypes) {
        return doRemove(AspectjExpressionPointcut.of(expression), aroundTypes);
    }

    @Override
    public boolean remove(Method method, Class<? extends Around>... aroundTypes) {
        return doRemove(MethodPointcut.of(method), aroundTypes);
    }

    @Override
    public void addEventListener(String expression, MethodListener listener) {
        addEventListener(new AspectjExpressionPointcut(expression), listener);
    }

    @Override
    public void addEventListener(Method method, MethodListener listener) {
        addEventListener(MethodPointcut.of(method), listener);
    }


    protected synchronized void doAdd(Pointcut pointcut, Around around) {
        Optional<AroundAdvisor> opt = Context.getAdvisor(pointcut, AroundAdvisor.class);
        AroundAdvisor advisor;
        if (opt.isPresent()) {
            advisor = opt.get();
            advisor.registerAround(around);
            return;
        }
        advisor = AroundAdvisor.of(pointcut, around);
        Context.ADVISORS.add(advisor);

        Set<Advisor> changes = new HashSet<>();
        changes.add(advisor);

        Event event = new PointcutEvent(changes);
        EventSource.EVENT_SOURCE.trigger(event);
    }

    public synchronized void addEventListener(Pointcut pointcut, MethodListener listener) {
        EventAdvisor advisorAdded = Context.addListener(pointcut, listener);
        if (advisorAdded == null) {//如果没添加一个新的advisor，而是只添加了MethodListener
            return;
        }

        EventSource.EVENT_SOURCE.trigger(new PointcutEvent(Collections.singleton(advisorAdded)));
    }

    protected boolean doRemove(Pointcut pointcut, Class<? extends Around>... aroundTypes) {
        Optional<Advisor> opt = Context.ADVISORS.stream()
                .filter(value -> value.getPointcut().equals(pointcut) && value instanceof AroundAdvisor)
                .findFirst();
        opt.ifPresent(advisor -> {
            if (advisor instanceof AroundAdvisor) {
                ((AroundAdvisor) advisor).removeAround(aroundTypes);
            }
        });
        return true;
    }


    @Override
    public synchronized boolean removeEventListener(Pointcut pointcut, Class<? extends MethodListener> listener) {
        Optional<Advisor> first = Context.ADVISORS.stream()
                .filter(value -> value instanceof EventAdvisor)
                .filter(pointcut::equals)
                .findFirst();

        if (first.isPresent()) {
            Advisor advisor = first.get();
            ((EventAdvisor) advisor).removeListener(listener);
            return true;
        }
        return false;
    }



}